package lv.solcraft.replication.server.boot;

import java.io.File;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;


import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.util.Assert;

/**
 * Booter is responsible for starting configured replication servers.
 * <p>
 * It reads configuration from REPLICATION_HOME/replication-server/servers.xml
 * file, parses it and then starts configured servers. One server startup
 * failure will fail whole booting.
 * <p>
 * REPLICATION_HOME can be set directly via <code>replication.home</code>
 * system property. It can also use Jboss server data directory (<code>jboss/servers/default/data</code>,
 * for example) if replication is deployed on JBoss server. In this case no
 * system property has to be configured.
 * <p>
 * Lifecycle methods <code>start</code> and <code>stop</code> are executed
 * from Spring application context (<code>applicationContext-boot.xml</code>).
 * 
 * @author Ilgvars
 */
public class Booter {

    private static final String SERVERS_DIR_NAME = "servers";

    private static Logger log = Logger.getLogger(Booter.class);

    private final Map<String, Server> servers = new ConcurrentHashMap<String, Server>();

    private final StartupOptions startupOptions;

    private File replicationServerHome;

    public Booter(StartupOptions startupOptions) {
        this.startupOptions = startupOptions;
    }

    public void start() throws Exception {
        log.info("Starting replication servers");
        log.info("Encoding " + System.getProperty("file.encoding"));
        Assert.isTrue("UTF-8".equals(System.getProperty("file.encoding")), "file.encoding must be UTF-8");
        replicationServerHome = new File(startupOptions.getReplHome() + File.separator + SERVERS_DIR_NAME);
        log.info("Replication server home: " + replicationServerHome);
        Assert.isTrue(replicationServerHome.exists(), "Replication home dir not found: " + replicationServerHome.getAbsolutePath());
        Assert.isTrue(replicationServerHome.isDirectory(), "Replication home dir is not actually a directory: " + replicationServerHome.getAbsolutePath());
        startServers();
        log.info("Replication servers started");
    }

    @SuppressWarnings("unchecked")
    protected void startServers() throws Exception {
        Collection<File> serverXmls = FileUtils.listFiles(replicationServerHome, new String[] { "server.xml" }, false);
        for (File file : serverXmls) {
            ServerConfig serverConfig = ServerConfig.fromXML(replicationServerHome, file);
            Server server = new Server(serverConfig);
            servers.put(serverConfig.getServerName(), server);
        }
    }

    public void stop() throws Exception {
        log.info("Stopping replication servers");
        stopServers();
        log.info("Stopped replication servers");
    }

    protected void stopServers() {
        for (Server server : servers.values()) {
            try {
                server.stop();
            } catch (Exception e) {
                log.warn("Error while stopping replication server: " + server.getServerConfig().getServerName(), e);
            }
        }
    }

    public Server getServer(String name) {
        Server server = servers.get(name);
        Assert.notNull(server, "Server not configured or started, server name: " + server);
        return server;
    }

    public Set<String> getServers() {
        return servers.keySet();
    }
}
